﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Xml;
using Epi.ImportExport;
using Epi.ImportExport.ProjectPackagers;
using CDC.SampleShell.Domain;

namespace CDC.SampleShell.Applications
{
    public sealed class ImportViewModel : ViewModelBase
    {
        private readonly Project _project;
        private readonly Epi.View _defaultForm;
        private string _importMessages = String.Empty;
        private double _progress = 0.0;
        private string _filePath = String.Empty;
        private bool _isImporting = false;

        public event EventHandler ImportStarted;
        public event EventHandler ImportStopped;

        public string FilePath
        {
            get
            {
                return _filePath;
            }
            set
            {
                _filePath = value;
                RaisePropertyChanged("FilePath");
            }
        }

        public string ImportMessages
        {
            get
            {
                return _importMessages;
            }
            private set
            {
                _importMessages = value;
                RaisePropertyChanged("ImportMessages");
            }
        }

        public double Progress
        {
            get
            {
                return _progress;
            }
            private set
            {
                _progress = value;
                RaisePropertyChanged("Progress");
            }
        }

        public ImportViewModel(Project project, Epi.View defaultForm)
        {
            // pre
            Contract.Requires(project != null);
            Contract.Requires(defaultForm != null);

            // post
            Contract.Ensures(_project != null);
            Contract.Ensures(_defaultForm != null);

            _project = project;
            _defaultForm = defaultForm;
        }

        private bool CanExecuteImportCommand()
        {
            if (_isImporting || String.IsNullOrEmpty(FilePath)) return false;
            return true;
        }

        public ICommand ImportCommand { get { return new RelayCommand(Import, CanExecuteImportCommand); } }
        private void Import()
        {
            var sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            ImportMessages = String.Empty;

            if (ImportStarted != null)
            {
                ImportStarted(this, new EventArgs());
            }

            _isImporting = true;

            ImportInfo importInfo = null;

            Task.Factory.StartNew(() =>
                {
                    try
                    {
                        XmlDocument doc = new XmlDocument();
                        doc.XmlResolver = null;
                        
                        string compressedText = Epi.Configuration.DecryptFileToString(FilePath, "DefaultPasswordPleaseChangeMe", "", "", 1000);
                        compressedText = compressedText.Remove(0, 24);
                        string uncompressedText = Epi.ImportExport.ImportExportHelper.UnZip(compressedText);
                        doc.LoadXml(uncompressedText);

                        XmlDataUnpackager unpackager = new XmlDataUnpackager(_defaultForm, doc);
                        unpackager.UpdateProgress += unpackager_UpdateProgress;
                        unpackager.MessageGenerated += unpackager_MessageGenerated;
                        unpackager.Unpackage();

                        unpackager.UpdateProgress -= unpackager_UpdateProgress;
                        unpackager.MessageGenerated -= unpackager_MessageGenerated;
                        importInfo = unpackager.ImportInfo;
                    }
                    catch (Exception ex)
                    {
                        // TODO: Add exception handling
                        AddMessage(String.Format("Critical error: {0}", ex.Message));
                    }
                    finally
                    {
                        if (ImportStopped != null)
                        {
                            ImportStopped(this, new EventArgs());
                        }

                        _isImporting = false;
                        Progress = 0.0;
                        FilePath = String.Empty;
                    }

                }, CancellationToken.None, TaskCreationOptions.LongRunning, System.Threading.Tasks.TaskScheduler.Default).ContinueWith(delegate
                {
                    sw.Stop();

                    if (sw.Elapsed.TotalSeconds < 60)
                    {
                        AddMessage(String.Format("Import completed in {0} seconds.", sw.Elapsed.TotalSeconds.ToString("F0")));
                    }
                    else
                    {
                        AddMessage(String.Format("Import completed in {0} minutes {1} seconds.", sw.Elapsed.TotalMinutes.ToString("F0"), sw.Elapsed.Seconds.ToString("F0")));
                    }

                    AddMessage(String.Format("In form {2}, {0} records inserted and {1} records updated.", 
                        importInfo.RecordsAppended[_defaultForm], 
                        importInfo.RecordsUpdated[_defaultForm],
                        _defaultForm.Name));
                }
                );
        }

        private void AddMessage(string message)
        {
            Contract.Requires(message != null);

            Contract.Ensures(ImportMessages != null);

            ImportMessages += (message + Environment.NewLine);
        }

        private void unpackager_MessageGenerated(string message)
        {
            AddMessage(message);
        }

        private void unpackager_UpdateProgress(double progress)
        {
            Contract.Requires(progress >= 0);

            Contract.Ensures(Progress >= 0);

            Progress = progress;
        }
    }
}
